﻿using HslCommunication.Profinet.Siemens;
using Microsoft.AspNetCore.Mvc;
using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
using Org.BouncyCastle.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Yitter.IdGenerator;
using ZhonTai.Admin.Core.Dto;
using ZhonTai.Admin.Services;
using ZhonTai.Common.Helpers;
using ZhonTai.DynamicApi;
using ZhonTai.DynamicApi.Attributes;
using ZhonTai.Industry.Contracts.Core.Enums;
using ZhonTai.Industry.Contracts.Model.Dic;
using ZhonTai.Industry.Core.Consts;
using ZhonTai.Industry.Domain.BlockOffset;
using ZhonTai.Industry.Domain.PLCBlock;
using ZhonTai.Industry.Domain.PLCDevice;
using ZhonTai.Industry.Domain.PLCOffset;
using ZhonTai.Industry.Domain.WorkStation;
using ZhonTai.Industry.Repositories.PLCDevice;
using ZhonTai.Industry.Services.BlockOffset.Dto.Input;
using ZhonTai.Industry.Services.BlockOffset.Dto.Output;
using ZhonTai.Industry.Services.PLCBlock.Dto.Input;
using ZhonTai.Industry.Services.PLCBlock.Dto.Output;
using ZhonTai.Industry.Services.PLCOffset.Dto.Input;

namespace ZhonTai.Industry.Services.BlockOffset
{
    /// <summary>
    /// 数据块偏移量关联服务
    /// </summary>
    [DynamicApi(Area = IndustryConsts.AreaName)]
    public class BlockOffsetService : BaseService, IBlockOffsetService, IDynamicApi
    {
        private IBlockOffsetRepository _blockOffsetRepository => LazyGetRequiredService<IBlockOffsetRepository>();
        private IWorkStationRepository _workStationRepository => LazyGetRequiredService<IWorkStationRepository>();
        private IPLCBlockRepository _plcBlockRepository => LazyGetRequiredService<IPLCBlockRepository>();
        private IPLCOffsetRepository _plcOffsetRepository => LazyGetRequiredService<IPLCOffsetRepository>();

        public BlockOffsetService()
        { }

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<BlockOffsetGetOutput> GetAsync(long id)
        {
            var output = await _blockOffsetRepository.GetAsync<BlockOffsetGetOutput>(id);
            return output;
        }

        /// <summary>
        /// 列表查询
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IEnumerable<BlockOffsetGetListOutput>> GetListAsync(BlockOffsetGetListInput input)
        {
            var list = await _blockOffsetRepository.Select
                .OrderByDescending(a => a.Id)
                .ToListAsync<BlockOffsetGetListOutput>();
            return list;
        }

        /// <summary>
        /// 获取偏移量ID集合
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<long[]> GetOffsetIdsAsync(BlockOffsetBlockIdInput input)
        {
            var select = await _blockOffsetRepository.Select
                .WhereIf(input.BlockId > 0, w => w.BlockId == input.BlockId)
                .ToListAsync();
            return select.Select(s => s.OffsetId).ToArray();
        }

        /// <summary>
        /// 分页查询
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<PageOutput<BlockOffsetGetPageOutput>> GetPageAsync(PageInput<BlockOffsetGetPageInput> input)
        {
            var filter = input.Filter;
            var select = _blockOffsetRepository.Select
                .WhereDynamicFilter(input.DynamicFilter)
                .WhereIf(filter.BlockId > 0, w => w.BlockId == filter.BlockId)
                .Count(out var total);
            if (input.SortList != null && input.SortList.Count > 0)
            {
                input.SortList.ForEach(sort =>
                {
                    select = select.OrderByPropertyNameIf(sort.Order.HasValue, sort.PropName, sort.IsAscending.Value);
                });
            }
            else
            {
                select = select.OrderBy(a => a.Sort);
            }
            var list = await select.OrderByDescending(c => c.Id)
              .Page(input.CurrentPage, input.PageSize)
              .ToListAsync(t => new BlockOffsetGetPageOutput() { Address = t.PLCBlock.Address, Offset = t.PLCOffset.ByteOffset, DataType = t.PLCOffset.DataType });
            if (filter.GetNewValue)
            {
                WorkStationEntity station = await _workStationRepository.GetAsync(filter.StationId);

                PLCBlockEntity block = await _plcBlockRepository.GetAsync(filter.BlockId);
                SiemensS7Net plc = InstanceDic.PLC[station.Id];
                if (station != null && plc != null)
                {
                    var operateResult = plc.Read("DB" + block.Address + "." + block.StartOffset, block.DataSize);
                    if (operateResult.IsSuccess)
                    {
                        var bytes = operateResult.Content;
                        list.ForEach(s =>
                        {
                            object? value;
                            switch (s.DataType)
                            {
                                case PLCDataTypeEnum.BOOL:
                                    value = GetPlcBool(plc, bytes, s.Offset);
                                    break;

                                case PLCDataTypeEnum.BYTE:
                                    value = plc.ByteTransform.TransByte(bytes, (int)s.Offset);
                                    break;

                                case PLCDataTypeEnum.WORD:
                                    value = plc.ByteTransform.TransUInt16(bytes, (int)s.Offset);
                                    break;

                                case PLCDataTypeEnum.DWORD:
                                    value = plc.ByteTransform.TransUInt32(bytes, (int)s.Offset);
                                    break;

                                case PLCDataTypeEnum.INT:
                                    value = plc.ByteTransform.TransInt16(bytes, (int)s.Offset);
                                    break;

                                case PLCDataTypeEnum.DINT:
                                    value = plc.ByteTransform.TransInt32(bytes, (int)s.Offset);
                                    break;

                                case PLCDataTypeEnum.REAL:
                                    value = plc.ByteTransform.TransSingle(bytes, (int)s.Offset);
                                    break;

                                case PLCDataTypeEnum.LREAL:
                                    value = plc.ByteTransform.TransDouble(bytes, (int)s.Offset);
                                    break;

                                case PLCDataTypeEnum.STRING:
                                    value = GetPlcString(plc, bytes, (short)s.Offset);
                                    break;

                                default:
                                    throw new InvalidOperationException($"Unsupported PLC data type: {s.DataType}");
                            }
                            s.CurrentValue = value.ToString();
                        });
                    }
                }
            }

            //关联查询代码
            var data = new PageOutput<BlockOffsetGetPageOutput> { List = list, Total = total };
            return data;
        }

        private bool GetPlcBool(SiemensS7Net plc, byte[] content, double index)
        {
            int integerPart = (int)index;
            int decimalPart = (int)((index - integerPart) * 10);
            int result = (integerPart * 8) + decimalPart;
            return plc.ByteTransform.TransBool(content, result);
        }

        private string GetPlcString(SiemensS7Net plc, byte[] content, short index)
        {
            byte length = content[index + 1];
            return plc.ByteTransform
                .TransString(content, index + 2, length, Encoding.ASCII)
                .Replace("\r", "")
                .Replace("\n", "");
        }

        /// <summary>
        /// 新增
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<long> AddAsync(BlockOffsetAddInput input)
        {
            var entity = Mapper.Map<BlockOffsetEntity>(input);
            var id = (await _blockOffsetRepository.InsertAsync(entity)).Id;

            return id;
        }

        /// <summary>
        /// 选择多条添加
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<long> AddMoreAsync(BlockOffsetsAddInput input)
        {
            var blockOffsetEntities = new List<BlockOffsetEntity>();

            foreach (var s in input.PlcOffsets)
            {
                BlockOffsetEntity blockOffsetEntity = new BlockOffsetEntity
                {
                    OffsetId = s,
                    BlockId = input.BlockId,
                    Another = StringHelper.GenerateRandom(8),
                    Enabled = true
                };
                blockOffsetEntities.Add(blockOffsetEntity);
            }

            // 批量插入（假设 _blockOffsetRepository 支持批量插入）
            await _blockOffsetRepository.InsertAsync(blockOffsetEntities);
            return 1;
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        [HttpPut]
        public async Task UpdateAsync(BlockOffsetUpdateInput input)
        {
            var result = await _blockOffsetRepository.Select.Where(a => a.BlockId == input.BlockId && a.Another == input.Another).CountAsync();
            if (result > 1)
            {
                throw ResultOutput.Exception($"当前数据块偏移量别名：{input.Another}已存在！");
            }
            var entity = await _blockOffsetRepository.GetAsync(input.Id);
            if (!(entity?.Id > 0))
            {
                throw ResultOutput.Exception("选择数据不存在！");
            }

            Mapper.Map(input, entity);
            await _blockOffsetRepository.UpdateAsync(entity);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpDelete]
        public async Task<bool> DeleteAsync(long id)
        {
            return await _blockOffsetRepository.DeleteAsync(id) > 0;
        }

        /// <summary>
        /// 软删除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpDelete]
        public async Task<bool> SoftDeleteAsync(long id)
        {
            return await _blockOffsetRepository.SoftDeleteAsync(id);
        }

        /// <summary>
        /// 批量软删除
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        [HttpPut]
        public async Task<bool> BatchSoftDeleteAsync(long[] ids)
        {
            return await _blockOffsetRepository.SoftDeleteAsync(ids);
        }

        /// <summary>
        /// 设置启用
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task SetEnableAsync(BlockOffsetSetEnableInput input)
        {
            var entity = await _blockOffsetRepository.GetAsync(input.BlockOffsetId);
            entity.Enabled = input.Enabled;
            await _blockOffsetRepository.UpdateAsync(entity);
        }

        /// <summary>
        /// 订阅启用
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task SetNoticeAsync(BlockOffsetSetNoticeInput input)
        {
            var entity = await _blockOffsetRepository.GetAsync(input.BlockOffsetId);
            entity.IsNoticed = input.Noticed;
            await _blockOffsetRepository.UpdateAsync(entity);
        }

        public async Task InsertOffsetValue(BlockOffsetInsertValueInput input)
        {
            var blockOffsetEntity = await _blockOffsetRepository.GetAsync(input.BlockOffsetId);
            if (blockOffsetEntity == null)
            {
                throw new Exception("未找到当前偏移量信息");
            }
            var offsetEntity = await _plcOffsetRepository.GetAsync(blockOffsetEntity.OffsetId);
            var BlockEntity = await _plcBlockRepository.GetAsync(blockOffsetEntity.BlockId);
            SiemensS7Net siemensS7 = InstanceDic.PLC[BlockEntity.WorkStationId];
            for (int i = 0; i < input.WriteCount; i++)
            {
                switch (offsetEntity.DataType)
                {
                    case PLCDataTypeEnum.BOOL:
                        await siemensS7.WriteAsync("DB" + BlockEntity.Address + "." + offsetEntity.ByteOffset, Convert.ToBoolean(input.WriteValue));
                        break;

                    case PLCDataTypeEnum.BYTE:
                        await siemensS7.WriteAsync("DB" + BlockEntity.Address + "." + offsetEntity.ByteOffset, Convert.ToByte(input.WriteValue));
                        break;

                    case PLCDataTypeEnum.DWORD:
                        await siemensS7.WriteAsync("DB" + BlockEntity.Address + "." + offsetEntity.ByteOffset, Convert.ToBoolean(input.WriteValue));
                        break;

                    case PLCDataTypeEnum.INT:
                        await siemensS7.WriteAsync("DB" + BlockEntity.Address + "." + offsetEntity.ByteOffset, Convert.ToInt16(input.WriteValue));
                        break;

                    case PLCDataTypeEnum.DINT:
                        await siemensS7.WriteAsync("DB" + BlockEntity.Address + "." + offsetEntity.ByteOffset, Convert.ToUInt16(input.WriteValue));
                        break;

                    case PLCDataTypeEnum.REAL:
                        await siemensS7.WriteAsync("DB" + BlockEntity.Address + "." + offsetEntity.ByteOffset, Convert.ToSingle(input.WriteValue));
                        break;

                    case PLCDataTypeEnum.LREAL:
                        await siemensS7.WriteAsync("DB" + BlockEntity.Address + "." + offsetEntity.ByteOffset, Convert.ToSingle(input.WriteValue));
                        break;

                    default:
                        throw new InvalidOperationException($"NOT FOUND DataError!");
                }
            }
        }
    }
}